# -*- coding: utf-8 -*-
from typing import List
import os
import uuid
import json

import aiofiles
from fastapi import Request, UploadFile, File, Form, BackgroundTasks
from loguru import logger
from pydantic import BaseModel

from configs import UPLOAD_FILE_DIR
from schemas.response_entity import JsonModel
from utils.hash_utils import cal_md5
from utils.file_utils import convert_docx_to_pdf, normalize_filename_extension
from db.repository.emergency_file import (
    db_get_ep_file_by_name,
    db_add_ep_file,
    db_get_ep_files,
    db_get_ep_files_by_user_groups,
    db_get_ep_file_by_id,
    db_update_ep_file_delete_state,
    db_update_ep_file_info,
    db_get_ep_files_by_origin_name,
)
from db.repository.user import db_get_user_group_ids

from .utils import parse_ep_pdf_background


# 应急预案文件上传
# 功能：管理员上传应急预案文件（pdf/doc/docx），自动去重、保存并触发后台解析
async def upload_emergency_plan_file(
        request: Request,
        background: BackgroundTasks,
        token=Form(...),
        file: UploadFile = File(...),
        scene_type: List[str] = Form(None),  # JSON list
        group_ids: List[int] = Form(None),   # JSON list
        accident_type: List[int] = Form(None),
):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()

    # 仅支持 pdf/doc/docx
    allowed_extensions = {'pdf', 'doc', 'docx', 'DOC', 'DOCX', 'PDF'}
    normalized_filename = normalize_filename_extension(file.filename)
    if '.' in normalized_filename:
        file_suffix = normalized_filename.rsplit('.', 1)[-1]
    else:
        file_suffix = ""

    if file_suffix not in allowed_extensions:
        return JsonModel(code=400, data=None, msg=f"不支持的文件类型: {file_suffix}，仅支持 {', '.join(allowed_extensions)}").to_response()

    # 计算 MD5 与大小
    file_content = await file.read()
    file_md5 = cal_md5(file_content)
    file_size = len(file_content)

    # 保存目录
    dir_path = os.path.join(UPLOAD_FILE_DIR, file_md5)
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)

    # 处理 doc/docx：先保存原始，再转 PDF；库里用 PDF 名称与路径
    if file_suffix in {"doc", "docx", "DOC", "DOCX"}:
        original_path = os.path.join(dir_path, normalized_filename)
        try:
            async with aiofiles.open(original_path, "wb") as f:
                await f.write(file_content)
        except IOError as e:
            logger.error(f"原始文件保存失败: {str(e)}")
            return JsonModel(code=500, data=None, msg="原始文件保存失败").to_response()

        pdf_hash_name = f"{file_md5}.pdf"
        pdf_path = os.path.join(dir_path, pdf_hash_name)

        if not os.path.exists(pdf_path):
            ok = convert_docx_to_pdf(original_path, pdf_path)
            if not ok:
                logger.error(f"Word 转 PDF 失败: {original_path}")
                return JsonModel(code=500, data=None, msg="Word 转 PDF 失败").to_response()

        file_hash_name = pdf_hash_name
        file_hash_path = pdf_path
        file_suffix = "pdf"

    else:
        # PDF：直接以 MD5 命名保存
        file_hash_name = f"{file_md5}.pdf"
        file_hash_path = os.path.join(dir_path, file_hash_name)
        try:
            async with aiofiles.open(file_hash_path, "wb") as f:
                await f.write(file_content)
        except IOError as e:
            logger.error(f"文件保存失败: {str(e)}")
            return JsonModel(code=500, data=None, msg="文件保存失败").to_response()

    # 存库前，通过 hash_name 判重
    file_obj = db_get_ep_file_by_name(file_hash_name)
    if file_obj:
        return JsonModel(code=403, data=None, msg="文件已存在，关联数据库即可").to_response()

    parsed_scene_type = []
    parsed_group_ids = []
    try:
        if scene_type:
            parsed_scene_type = []
            if isinstance(scene_type, str):
                parsed_scene_type = json.loads(scene_type)
            elif isinstance(scene_type, List):
                parsed_scene_type = scene_type
    except Exception:
        logger.warning(f"Invalid scene_type: {scene_type}")
        parsed_scene_type = []
    try:
        if group_ids:
            parsed_group_ids = []
            if isinstance(group_ids, str):
                parsed_group_ids = json.loads(group_ids)
            elif isinstance(group_ids, List):
                parsed_group_ids = group_ids
    except Exception:
        logger.warning(f"Invalid group_ids: {group_ids}")
        parsed_group_ids = []

    file_obj, _ = db_add_ep_file(
        user.id,
        file_hash_name,
        normalized_filename,
        file_hash_path,
        file_suffix,
        file_size,
        scene_type=parsed_scene_type,
        group_ids=parsed_group_ids,
        accident_type=accident_type,
    )
    # 可在此添加后台解析任务（若有）
    scene_type = '321' if "I级响应" in parsed_scene_type else '32' if "II级响应" in parsed_scene_type else '3'
    background.add_task(parse_ep_pdf_background, file_obj, file_hash_path, scene_type, delete_old_file=True)
    return JsonModel(code=200, data={"id": str(file_obj.id)}, msg=f"上传文件:{normalized_filename}成功").to_response()


# 查询应急预案文件列表
# 功能：根据角色返回可见文件（超级管理员全部，其余按用户组过滤）
async def list_emergency_plan_files(request: Request, page: int = 1, page_size: int = 50, name: str = ""):
    user = request.state.user
    try:
        offset = (page - 1) * page_size
        if user.check_superman_role():
            files, total = db_get_ep_files(name_like=name or None, offset=offset, limit=page_size)
        else:
            # 非超级管理员只能查看该用户所在组的可查看文件
            user_group_ids = db_get_user_group_ids(user)
            files, total = db_get_ep_files_by_user_groups(user_group_ids, name_like=name or None, offset=offset, limit=page_size)
        
        return JsonModel(code=200, data={
            "items": [f.to_json() for f in files],
            "total": total,
            "page": page,
            "page_size": page_size
        }, msg="success").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="查询失败").to_response()


# 获取应急预案文件详情
# 功能：按文件ID获取详情，仅管理员可查
async def get_emergency_plan_file_by_id(request: Request, file_id: uuid.UUID):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        f = db_get_ep_file_by_id(file_id)
        if not f or getattr(f, 'is_delete', False):
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        return JsonModel(code=200, data=f.to_json(), msg="success").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="查询失败").to_response()


# 按名称搜索应急预案文件
# 功能：支持按原始文件名模糊查询，仅管理员可查
async def search_emergency_plan_files(request: Request, filename: str = ""):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        files = db_get_ep_files_by_origin_name(filename)
        return JsonModel(code=200, data=[f.to_json() for f in files], msg="success").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="查询失败").to_response()


class UpdateEmergencyPlanFileEntity(BaseModel):
    file_id: uuid.UUID
    name: str | None = None
    scene_type: List[str] | None = None
    group_ids: List[int] | None = None
    accident_type: List[int] | None = None


# 更新应急预案文件信息
# 功能：管理员更新名称、场景类型、用户组、事故类型等信息
async def update_emergency_plan_file(request: Request, payload: UpdateEmergencyPlanFileEntity):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    if not any([
        payload.name,
        payload.scene_type is not None,
        payload.group_ids is not None,
        payload.accident_type is not None,
    ]):
        return JsonModel(code=300, data=None, msg="缺少更新内容").to_response()
    try:
        f = db_update_ep_file_info(
            payload.file_id,
            name=payload.name,
            scene_type=payload.scene_type,
            group_ids=payload.group_ids,
            accident_type=payload.accident_type,
        )
        if not f:
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        return JsonModel(code=200, data=None, msg="更新成功").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="更新失败").to_response()


# 删除应急预案文件（软删）
# 功能：管理员将文件标记为删除
async def delete_emergency_plan_file(request: Request, file_id: uuid.UUID):
    user = request.state.user
    if not user.check_admin_role():
        return JsonModel(code=401, data=None, msg=f"用户无权限").to_response()
    try:
        success = db_update_ep_file_delete_state(file_id, True)
        if not success:
            return JsonModel(code=404, data=None, msg="文件不存在").to_response()
        return JsonModel(code=200, data=None, msg="删除成功").to_response()
    except Exception as e:
        logger.error(e)
        return JsonModel(code=500, data=None, msg="删除失败").to_response()

